function MVVM(options) {
	// options就是配置对象
	this.$options = options || {};
	// 原data数据
	var data = (this._data = this.$options.data);
	var me = this;

	// 1. 数据代理：将data中的数据代理到this，将来可以通过this直接访问data数据
	// Object.keys(data) 提取data数据所有属性名，成一个数组: ['num']
	// 遍历数组 ['num'].forEach
	// 总结：Object.keys(data).forEach 为了给对象遍历（取缔for in循环）
	Object.keys(data).forEach(function (key) {
		// 对data数据中每个属性进行数据代理
		me._proxyData(key);
	});

	// 数据代理：代理计算属性（了解）
	this._initComputed();

	// 2. 数据劫持：对原data数据进行重新定义，定义成响应式
	observe(data, this);

	// 3. 模板解析：解析模板页面中模板语法（插值语法和指令语法）
	this.$compile = new Compile(options.el || document.body, this);
}

MVVM.prototype = {
	constructor: MVVM,
	$watch: function (key, cb, options) {
		new Watcher(this, key, cb);
	},
	// 数据代理方法
	_proxyData: function (key, setter, getter) {
		var me = this;
		setter =
			setter ||
			// 通过Object.defineProperty方法，给this添加data中的属性，属性值定义get/set
			// 当你通过this读取数据时。触发数据代理设置的get，实际读取原data数据的值
			// 当你通过this设置数据时。触发数据代理设置的set，实际设置原data数据的值
			Object.defineProperty(me, key, {
				configurable: false,
				enumerable: true,
				get: function proxyGetter() {
					return me._data[key]; // 读取原data数据
				},
				set: function proxySetter(newVal) {
					me._data[key] = newVal; // 更新原data数据
				},
			});
	},

	_initComputed: function () {
		var me = this;
		var computed = this.$options.computed;
		if (typeof computed === "object") {
			Object.keys(computed).forEach(function (key) {
				Object.defineProperty(me, key, {
					get:
						typeof computed[key] === "function"
							? computed[key]
							: computed[key].get,
					set: function () {},
				});
			});
		}
	},
};
